package eva

import (
	"testing"
	"gitcode.com/deyiyangyang/gascheme/lex"
	"gitcode.com/deyiyangyang/gascheme/obj"
	"gitcode.com/deyiyangyang/gascheme/par"
)

func TestCharPredicate(t *testing.T) {
	tests := []struct {
		input    string
		expected bool
	}{
		{`(char? #\a)`, true},
		{`(char? #\A)`, true},
		{`(char? #\0)`, true},
		{`(char? 65)`, false},
		{`(char? "a")`, false},
		{`(char? #t)`, false},
	}

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		parser := par.NewParser(lexer)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser error: %s", err)
		}

		env := obj.NewGlobalEnvironment()
		RegisterBuiltinProcedures(env)

		result := Eval(expr, env)

		boolean, ok := result.(*obj.Boolean)
		if !ok {
			t.Fatalf("expected boolean, got %T", result)
		}

		if boolean.Value != tt.expected {
			t.Errorf("input %s: expected %t, got %t", tt.input, tt.expected, boolean.Value)
		}
	}
}

func TestCharEqual(t *testing.T) {
	tests := []struct {
		input    string
		expected bool
	}{
		{`(char=? #\a #\a)`, true},
		{`(char=? #\a #\b)`, false},
		{`(char=? #\A #\a)`, false},
		{`(char=? #\0 #\0)`, true},
	}

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		parser := par.NewParser(lexer)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser error: %s", err)
		}

		env := obj.NewGlobalEnvironment()
		RegisterBuiltinProcedures(env)

		result := Eval(expr, env)

		boolean, ok := result.(*obj.Boolean)
		if !ok {
			t.Fatalf("expected boolean, got %T", result)
		}

		if boolean.Value != tt.expected {
			t.Errorf("input %s: expected %t, got %t", tt.input, tt.expected, boolean.Value)
		}
	}
}

func TestCharLess(t *testing.T) {
	tests := []struct {
		input    string
		expected bool
	}{
		{`(char<? #\a #\b)`, true},
		{`(char<? #\b #\a)`, false},
		{`(char<? #\a #\a)`, false},
		{`(char<? #\0 #\1)`, true},
		{`(char<? #\A #\a)`, true}, // Uppercase letters come before lowercase in ASCII
	}

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		parser := par.NewParser(lexer)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser error: %s", err)
		}

		env := obj.NewGlobalEnvironment()
		RegisterBuiltinProcedures(env)

		result := Eval(expr, env)

		boolean, ok := result.(*obj.Boolean)
		if !ok {
			t.Fatalf("expected boolean, got %T", result)
		}

		if boolean.Value != tt.expected {
			t.Errorf("input %s: expected %t, got %t", tt.input, tt.expected, boolean.Value)
		}
	}
}

func TestCharGreater(t *testing.T) {
	tests := []struct {
		input    string
		expected bool
	}{
		{`(char>? #\b #\a)`, true},
		{`(char>? #\a #\b)`, false},
		{`(char>? #\a #\a)`, false},
		{`(char>? #\1 #\0)`, true},
		{`(char>? #\a #\A)`, true}, // Lowercase letters come after uppercase in ASCII
	}

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		parser := par.NewParser(lexer)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser error: %s", err)
		}

		env := obj.NewGlobalEnvironment()
		RegisterBuiltinProcedures(env)

		result := Eval(expr, env)

		boolean, ok := result.(*obj.Boolean)
		if !ok {
			t.Fatalf("expected boolean, got %T", result)
		}

		if boolean.Value != tt.expected {
			t.Errorf("input %s: expected %t, got %t", tt.input, tt.expected, boolean.Value)
		}
	}
}

func TestCharLessEqual(t *testing.T) {
	tests := []struct {
		input    string
		expected bool
	}{
		{`(char<=? #\a #\b)`, true},
		{`(char<=? #\b #\a)`, false},
		{`(char<=? #\a #\a)`, true},
		{`(char<=? #\0 #\1)`, true},
	}

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		parser := par.NewParser(lexer)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser error: %s", err)
		}

		env := obj.NewGlobalEnvironment()
		RegisterBuiltinProcedures(env)

		result := Eval(expr, env)

		boolean, ok := result.(*obj.Boolean)
		if !ok {
			t.Fatalf("expected boolean, got %T", result)
		}

		if boolean.Value != tt.expected {
			t.Errorf("input %s: expected %t, got %t", tt.input, tt.expected, boolean.Value)
		}
	}
}

func TestCharGreaterEqual(t *testing.T) {
	tests := []struct {
		input    string
		expected bool
	}{
		{`(char>=? #\b #\a)`, true},
		{`(char>=? #\a #\b)`, false},
		{`(char>=? #\a #\a)`, true},
		{`(char>=? #\1 #\0)`, true},
	}

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		parser := par.NewParser(lexer)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser error: %s", err)
		}

		env := obj.NewGlobalEnvironment()
		RegisterBuiltinProcedures(env)

		result := Eval(expr, env)

		boolean, ok := result.(*obj.Boolean)
		if !ok {
			t.Fatalf("expected boolean, got %T", result)
		}

		if boolean.Value != tt.expected {
			t.Errorf("input %s: expected %t, got %t", tt.input, tt.expected, boolean.Value)
		}
	}
}

func TestCharErrorCases(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{`(char=?)`, "char=? requires exactly 2 arguments"},
		{`(char=? #\a)`, "char=? requires exactly 2 arguments"},
		{`(char=? #\a #\b #\c)`, "char=? requires exactly 2 arguments"},
		{`(char=? #\a 65)`, "char=? arguments must be characters"},
		{`(char=? "a" #\b)`, "char=? arguments must be characters"},
		{`(char<? #\a)`, "char<? requires exactly 2 arguments"},
		{`(char>? #\a)`, "char>? requires exactly 2 arguments"},
		{`(char<=? #\a)`, "char<=? requires exactly 2 arguments"},
		{`(char>=? #\a)`, "char>=? requires exactly 2 arguments"},
		{`(char?)`, "char? requires exactly 1 argument"},
	}

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		parser := par.NewParser(lexer)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser error: %s", err)
		}

		env := obj.NewGlobalEnvironment()
		RegisterBuiltinProcedures(env)

		result := Eval(expr, env)

		errorObj, ok := result.(*obj.Error)
		if !ok {
			t.Fatalf("expected error, got %T", result)
		}

		if errorObj.Message != tt.expected {
			t.Errorf("input %s: expected error message '%s', got '%s'", tt.input, tt.expected, errorObj.Message)
		}
	}
}